from flask import Flask, request, jsonify, render_template
from flask_cors import CORS
import os
import sys
import re
import json
import asyncio
from pathlib import Path
from typing import Optional
from browser_use import Browser, Agent
from browser_use.browser.context import BrowserContextConfig
from browser_use import BrowserConfig
from langchain_openai import ChatOpenAI
from pydantic import SecretStr
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()

app = Flask(__name__)
CORS(app)

# 配置
PORT = int(os.environ.get('PORT', 5100))

class ArticleAnalyzer:
    """文章分析器类，用于分析网页文章内容并生成结构化大纲"""
    
    def __init__(self):
        self.browser: Optional[Browser] = None
        self.llm = ChatOpenAI(
            base_url='https://api.deepseek.com/v1',
            model='deepseek-chat',
            api_key=SecretStr(os.getenv("DEEPSEEK_API_KEY")),
            temperature=0.1
        )
        
    async def setup_browser(self) -> None:
        """设置并初始化浏览器"""
        print("正在初始化浏览器配置...")
        
        # 配置浏览器上下文
        context_config = BrowserContextConfig(
            # 页面加载设置
            minimum_wait_page_load_time=0.5,
            wait_for_network_idle_page_load_time=2.0,  # 减少等待时间
            maximum_wait_page_load_time=5.0,  # 减少最大等待时间
            
            # 显示设置
            browser_window_size={'width': 1280, 'height': 1100},
            locale='zh-CN',
            highlight_elements=True,
            viewport_expansion=800,
            
            # 用户代理设置
            user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
        )
        
        # 浏览器基础配置
        browser_config = BrowserConfig(
            headless=False,
            disable_security=True,
            extra_chromium_args=[
                '--no-sandbox',
                '--disable-dev-shm-usage',
                '--disable-blink-features=AutomationControlled',
                '--start-maximized',
                '--disable-web-security',
                '--disable-features=IsolateOrigins,site-per-process',
                '--disable-gpu',  # 禁用GPU加速
                '--disable-software-rasterizer',  # 禁用软件光栅化
                '--disable-extensions',  # 禁用扩展
                '--disable-default-apps',  # 禁用默认应用
                '--no-first-run',  # 禁用首次运行配置
                '--no-default-browser-check'  # 禁用默认浏览器检查
            ],
            new_context_config=context_config
        )
        
        try:
            print("正在启动浏览器...")
            self.browser = Browser(config=browser_config)
            self.context = await self.browser.new_context()
            print("浏览器初始化成功！")
        except Exception as e:
            raise RuntimeError(f"浏览器初始化失败: {str(e)}")

    def clean_markdown_content(self, content: str) -> str:
        """清理和格式化 markdown 内容"""
        if not content:
            print("警告：传入的内容为空")
            return ""
            
        try:
            # 确保内容是字符串
            content = str(content).strip()
            
            # 如果内容为空，返回提示信息
            if not content:
                return "# 无法提取有效内容\n\n可能的原因：\n- 页面加载不完整\n- 内容被保护\n- 格式不支持\n"
            
            # 确保内容以换行符结束
            if not content.endswith('\n'):
                content += '\n'
                
            return content
            
        except Exception as e:
            print(f"清理内容时出错: {str(e)}")
            return str(content).strip() + '\n'

    async def analyze_url(self, url: str, timeout: int = 60) -> Optional[str]:
        """分析指定URL的文章内容"""
        if not url.startswith(('http://', 'https://')):
            url = 'https://' + url
            
        print(f"正在访问URL: {url}")
        
        try:
            if not self.browser:
                await self.setup_browser()
                
            agent = Agent(
                task=(
                    f'任务：提取并分析文章内容\n'
                    f'步骤：\n'
                    f'1. 访问URL：{url}\n'
                    f'2. 等待页面加载完成\n'
                    f'3. 定位文章主体内容：\n'
                    f'   - 查找文章标题元素\n'
                    f'   - 查找文章正文容器\n'
                    f'   - 识别文章大纲或目录结构\n'
                    f'4. 提取内容并转换为Markdown：\n'
                    f'   - 标题使用 # 格式\n'
                    f'   - 章节使用 ## 和 ### 格式\n'
                    f'   - 重点内容使用 - 列表\n'
                    f'   - 保留原文代码块和引用\n'
                    f'   - 保持段落结构完整\n'
                    f'5. 清理无关内容：\n'
                    f'   - 移除广告\n'
                    f'   - 移除评论区\n'
                    f'   - 移除导航栏\n'
                    f'   - 移除页脚\n'
                    f'6. 直接返回Markdown格式文本\n'
                    f'要求：\n'
                    f'- 确保提取完整的文章内容\n'
                    f'- 保持文章结构清晰\n'
                    f'- 内容必须有实际文本\n'
                    f'- 直接返回Markdown格式文本'
                ),
                llm=self.llm,
                browser=self.browser,
                browser_context=self.context,
                use_vision=False
            )
            
            try:
                print("正在提取文章内容...")
                result = await asyncio.wait_for(agent.run(), timeout=timeout)
                print("内容提取完成")
                
                if not result:
                    print("警告：提取结果为空")
                    return None
                
                print(f"结果类型: {type(result)}")
                print(f"结果内容: {str(result)[:200]}")  # 打印前200个字符
                
                # 尝试将结果转换为字符串并解析
                result_str = str(result)
                if "AgentHistoryList" in result_str:
                    print("检测到 AgentHistoryList 格式")
                    try:
                        # 尝试提取最后一个成功的结果
                        if "is_done=True, success=True" in result_str:
                            print("找到成功完成的结果")
                            # 使用字符串处理提取 extracted_content
                            start_idx = result_str.rfind("extracted_content='") + len("extracted_content='")
                            if start_idx > len("extracted_content='"):
                                end_idx = result_str.find("'", start_idx)
                                if end_idx > start_idx:
                                    content = result_str[start_idx:end_idx]
                                    print("成功提取内容")
                                else:
                                    print("未找到内容结束位置")
                                    content = None
                            else:
                                print("未找到内容起始位置")
                                content = None
                        else:
                            print("未找到成功完成的结果")
                            content = None
                    except Exception as e:
                        print(f"提取内容时出错: {str(e)}")
                        content = None
                else:
                    print("使用原始结果")
                    content = result_str
                
                if not content:
                    print("警告：未能提取到有效内容")
                    return None
                
                # 处理转义字符
                try:
                    # 替换转义序列
                    content = content.replace('\\n', '\n')
                    content = content.replace('\\t', '\t')
                    content = content.replace('\\"', '"')
                    content = content.replace("\\'", "'")
                    content = content.replace('\\\\', '\\')
                except Exception as e:
                    print(f"处理转义字符时出错: {str(e)}")
                
                # 检查内容是否为空
                if not content.strip():
                    print("警告：提取的内容为空字符串")
                    return None
                
                print(f"提取的内容长度: {len(content)}")
                print("内容前100个字符预览:")
                print(content[:100])
                    
                # 保存结果到文件
                output_path = Path("article_structure.md")
                try:
                    # 使用UTF-8编码保存文件
                    with open(output_path, 'w', encoding='utf-8') as f:
                        f.write(content)
                    print(f"\n分析完成！结果已保存到：{output_path}")
                    return content
                except Exception as e:
                    print(f"保存文件时出错: {str(e)}")
                    return content
                    
            except asyncio.TimeoutError:
                print(f"提取超时（{timeout}秒），正在重试...")
                return None
                
        except Exception as e:
            print(f"分析过程发生错误: {str(e)}")
            return None
        finally:
            try:
                # 每次分析完成后关闭浏览器，避免资源占用
                if self.browser:
                    await self.context.close()
                    await self.browser.close()
                    self.browser = None
                    print("浏览器已关闭")
            except Exception as e:
                print(f"关闭浏览器时出错: {str(e)}")

    async def close(self):
        """关闭浏览器及相关资源"""
        if self.browser:
            try:
                await self.context.close()
                await self.browser.close()
            except Exception as e:
                print(f"关闭浏览器时出错: {str(e)}")

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/api/generate-mindmap', methods=['POST'])
def generate_mindmap():
    """处理文章结构生成请求"""
    try:
        # 验证请求数据
        if not request.is_json:
            return jsonify({
                'success': False,
                'error': '请求格式必须是 JSON'
            }), 400
            
        data = request.get_json()
        if not data or 'url' not in data:
            return jsonify({
                'success': False,
                'error': '请提供有效的 URL'
            }), 400
            
        url = data['url'].strip()
        if not url:
            return jsonify({
                'success': False,
                'error': 'URL 不能为空'
            }), 400
            
        # 创建事件循环
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        
        try:
            # 创建分析器实例
            analyzer = ArticleAnalyzer()
            
            # 运行异步分析
            content = loop.run_until_complete(analyzer.analyze_url(url))
            
            if not content:
                return jsonify({
                    'success': False,
                    'error': '无法提取文章内容'
                }), 404
                
            # 保存到文件
            try:
                with open('article_structure.md', 'w', encoding='utf-8') as f:
                    f.write(content)
            except Exception as e:
                print(f"保存文件时出错: {str(e)}")
                # 文件保存失败不影响 API 响应
                
            # 返回成功响应
            return jsonify({
                'success': True,
                'mindmap': content
            })
            
        except Exception as e:
            print(f"分析文章时出错: {str(e)}")
            return jsonify({
                'success': False,
                'error': f'分析文章时出错: {str(e)}'
            }), 500
            
        finally:
            loop.close()
                    
    except Exception as e:
        print(f"处理请求时出错: {str(e)}")
        return jsonify({
            'success': False,
            'error': f'服务器错误: {str(e)}'
        }), 500

# 添加错误处理器
@app.errorhandler(404)
def not_found_error(error):
    return jsonify({
        'success': False,
        'error': '请求的资源不存在'
    }), 404

@app.errorhandler(500)
def internal_error(error):
    return jsonify({
        'success': False,
        'error': '服务器内部错误'
    }), 500

@app.errorhandler(Exception)
def handle_exception(e):
    print(f"未处理的异常: {str(e)}")
    return jsonify({
        'success': False,
        'error': '服务器发生未知错误'
    }), 500

if __name__ == '__main__':
    # 检查API key是否已设置
    if not os.getenv("DEEPSEEK_API_KEY"):
        print("错误：需要设置DEEPSEEK_API_KEY环境变量")
        sys.exit(1)
        
    app.run(host='0.0.0.0', port=PORT, debug=True) 